package com.kasax.plugin.app.visitor.lifecycle

import com.kasax.plugin.app.LogUtils
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.commons.LocalVariablesSorter

/**
 * 自定义 MethodVisitor，用于在 Activity 的 onDestroy 方法中插入日志记录逻辑。
 * 核心功能：
 * 1. 记录方法开始时间。
 * 2. 在方法结束时计算耗时并记录日志。
 * 3. 日志格式："类名.onDestroy time: 耗时"。
 */
class CustomDestoryMethodVisitor extends LocalVariablesSorter {

    private String TAG = CustomDestoryMethodVisitor.class.simpleName
    private String className
    private String mName
    private int timeLocalIndex

    public CustomDestoryMethodVisitor(MethodVisitor methodVisitor, String clsName, int access, String methodname,
                                     String descriptor) {

        super(Opcodes.ASM7, access, descriptor, methodVisitor)
        this.className = clsName
        this.mName = methodname
        LogUtils.d(TAG, "CustomDestoryMethodVisitor init class-methodName: $className , methodname: $methodname")
    }

    @Override
    void visitCode() {
        super.visitCode()
        if (mv == null) {
            throw new IllegalStateException("MethodVisitor (mv) is null in visitCode")
        }
        timeLocalIndex = newLocal(Type.LONG_TYPE)
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false)
        mv.visitVarInsn(Opcodes.LSTORE, timeLocalIndex)
    }

    @Override
    void visitInsn(int opcode) {
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false)
        mv.visitVarInsn(Opcodes.LLOAD, timeLocalIndex)
        // 此处的值在栈顶
        mv.visitInsn(Opcodes.LSUB)
        // 因为后面要用到这个值所以先将其保存到本地变量表中
        mv.visitVarInsn(Opcodes.LSTORE, timeLocalIndex)
        def stringBuilderIndex = newLocal(Type.getObjectType("java/lang/StringBuilder"))
        mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder")
        mv.visitInsn(Opcodes.DUP)
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false)
        // 需要将栈顶的 stringbuilder 指针保存起来否则后面找不到了
        mv.visitVarInsn(Opcodes.ASTORE, stringBuilderIndex)
        def simpleClassName = className.substring(className.lastIndexOf('.') + 1)
        mv.visitVarInsn(Opcodes.ALOAD, stringBuilderIndex)
        mv.visitLdcInsn(simpleClassName + "." + mName + " time:")
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false)
        mv.visitInsn(Opcodes.POP)
        mv.visitVarInsn(Opcodes.ALOAD, stringBuilderIndex)
        mv.visitVarInsn(Opcodes.ALOAD, timeLocalIndex)
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false)
        mv.visitInsn(Opcodes.POP)
        mv.visitLdcInsn("zzg")
        mv.visitVarInsn(Opcodes.ALOAD, stringBuilderIndex)
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
        // 注意： Log.d 方法是有返回值的，需要 pop 出去
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "android/util/Log", "d", "(Ljava/lang/String;Ljava/lang/String;)I", false)
        mv.visitInsn(Opcodes.POP)
        super.visitInsn(opcode)
    }
}